package monitor

import (
	"bufio"
	"encoding/json"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/ochinchina/supervisord/config"
	lib "github.com/ochinchina/supervisord/lib"
	"github.com/ochinchina/supervisord/tool"
	"github.com/ochinchina/supervisord/util"
	log "github.com/sirupsen/logrus"

	"database/sql"

	_ "github.com/mattn/go-sqlite3"
)

var dataPath = ""
var dbFilePath = ""

func InitDB(c *config.Config) {
	dataPath := c.ParseConfigStringExpression(c.Config.Monitor.DataPath)
	filePath := dataPath + "/event.db"
	dbFilePath = filePath
	// os.Remove(filePath)
	if !checkFileIsExist(filePath) {
		db, err := sql.Open("sqlite3", filePath)
		if err != nil {
			log.Fatal(err)
		}
		defer db.Close()

		sqlStmt := `
		create table event_log (uid INTEGER PRIMARY KEY AUTOINCREMENT, event_time INTEGER, type_ text, name text, desc text, log_name text);
		`
		_, err = db.Exec(sqlStmt)
		if err != nil {
			log.Printf("%q: %s\n", err, sqlStmt)
			return
		}
	}
}

// func RecordEvent(e_type string, name string, desc string, c *config.Entry) {
// }

// 事件类型，事件名，事件简述(可选)，事件日志(可选)
// 类型1 进程启停：ID 中文名
// P0 启动成功
// P1 启动最大次数失败
// P2 进程非手动退出
// P3 进程手动退出
func RecordEvent(e_type string, name string, desc string, c *config.Entry) {
	now := time.Now()
	cfg := getEventConfig(e_type, c)
	if cfg == nil {
		log.WithFields(log.Fields{"event": e_type, "name": name}).Info(desc)
		insertDB(now.UnixNano()/1e6, e_type, name, desc, "", false)
		return
	}

	alarm := false
	for _, ac := range cfg.Actions {
		if strings.HasPrefix(ac, "alarm_dingtalk") {
			alarm = true
		}
	}

	if dataPath == "" {
		dataPath = c.ParseProgramStringExpression(c.Config.Monitor.DataPath)
	}

	fileName := now.Format("2006-01-02-15-04-05.000") + "__" + e_type + "__" + name + ".log"
	filePath := dataPath + "/" + fileName[0:10]
	err := os.MkdirAll(filePath, os.ModePerm)
	if err != nil {
		log.Errorf("can not create monitor log: %s, %v", filePath, err)
		insertDB(now.UnixNano()/1e6, e_type, name, desc, "", alarm)
		return
	}
	filePath = filePath + "/" + fileName
	file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		log.Errorf("can not create monitor log: %s, %v", filePath, err)
		insertDB(now.UnixNano()/1e6, e_type, name, desc, "", alarm)
		return
	}
	defer file.Close()
	//写入文件时，使用带缓存的 *Writer
	write := bufio.NewWriter(file)

	for _, ac := range cfg.Actions {
		if strings.HasPrefix(ac, "save_log") {
			logsStr := c.GetStringExpression(ac, "")
			//log.Infof("qqq lookup log config: %v", logsStr)
			if logsStr != "" {
				// env := tool.NewStringExpression("program_name", name)
				configFileDir := filepath.Dir(c.GetStdoutLogfile())
				logs := strings.Split(logsStr, ",")
				logFiles := tool.GetIncludeFiles(configFileDir, nil, logs)
				//log.Infof("qqq start to read logfile: %v", logFiles)
				for _, lo := range logFiles {
					lines, err := util.ReadLastRows(lo, 200)
					if err == nil {
						write.WriteString("\n----------------------TailFile: " + lo + "\n")
						write.WriteString(lines)
						write.Flush()
					} else {
						log.Errorf("can not read log file: %s, %v", lo, err)
					}
				}
			}
		}
	}

	insertDB(now.UnixNano()/1e6, e_type, name, desc, fileName, alarm)
}

// 类型2 告警：
// A1 进程CPU/内存超限
// A2 系统CPU/内存/磁盘/负载超限
func RecordEventProcessAlarm(name string, desc string, c *config.Config) {
	e_type := "A1"
	alarm := false
	cfg := getEventConfig(e_type, c.GetProgram(name))
	if cfg != nil {
		for _, ac := range cfg.Actions {
			if strings.HasPrefix(ac, "alarm_dingtalk") {
				alarm = true
			}
		}
	}
	now := time.Now()
	fileName := writerMonitorLog(now, e_type, name, c)
	insertDB(now.UnixNano()/1e6, e_type, name, desc, fileName, alarm)
}

func RecordEventOsAlarm(desc string, c *config.Config) {
	e_type := "A2"
	alarm := false
	if c.Config.Monitor.EventOs.Actions != nil {
		for _, ac := range c.Config.Monitor.EventOs.Actions {
			if strings.HasPrefix(ac, "alarm_dingtalk") {
				alarm = true
			}
		}
	}

	now := time.Now()
	fileName := writerMonitorLog(now, e_type, "OS", c)
	insertDB(now.UnixNano()/1e6, e_type, "OS", desc, fileName, alarm)
}

func writerMonitorLog(now time.Time, e_type string, name string, c *config.Config) string {
	monitorFilePath := getStdoutLogfile2(c)
	lines, err := util.ReadLastRows(monitorFilePath, 200)
	if err == nil {
		return writerEventLog(now, e_type, name, monitorFilePath, lines, c)
	} else {
		log.Errorf("can not read log file: %s, %v", monitorFilePath, err)
		return ""
	}
}

func writerEventLog(now time.Time, e_type, name, readFile, readLines string, c *config.Config) string {

	if dataPath == "" {
		dataPath = c.ParseConfigStringExpression(c.Config.Monitor.DataPath)
	}

	fileName := now.Format("2006-01-02-15-04-05.000") + "__" + e_type + "__" + name + ".log"
	filePath := dataPath + "/" + fileName[0:10]
	err := os.MkdirAll(filePath, os.ModePerm)
	if err != nil {
		log.Errorf("can not create monitor log: %s, %v", filePath, err)
		return ""
	}
	filePath = filePath + "/" + fileName
	file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		log.Errorf("can not create monitor log: %s, %v", filePath, err)
		return ""
	}
	defer file.Close()
	//写入文件时，使用带缓存的 *Writer
	write := bufio.NewWriter(file)

	_, err = write.WriteString("\n----------------------TailFile: " + readFile + "\n")
	if err == nil {
		write.WriteString(readLines)
		write.Flush()
	} else {
		log.Errorf("can not write log file: %s, %v", filePath, err)
	}
	return fileName
}

func checkFileIsExist(filename string) bool {
	var exist = true
	if _, err := os.Stat(filename); os.IsNotExist(err) {
		exist = false
	}
	return exist
}

type EventLog struct {
	Uid       int64
	EventTime int64
	Type      string
	Name      string
	Desc      string
	LogName   string
}

func QueryEvent(qevent_time_start int64, qevent_time_end int64, qtype_, qname string) ([]EventLog, error) {
	db, _ := sql.Open("sqlite3", dbFilePath)

	if qevent_time_start == 0 {
		// 默认查询最近1分钟
		qevent_time_start = time.Now().UnixNano()/1e6 - 1000*60
	}
	params := make([]interface{}, 4)
	idx := 0
	params[idx] = qevent_time_start
	idx++
	// SELECT * FROM event_log WHERE event_time > 1627373413000 AND type_='A1' AND name='testapp.jar';
	sql := "select * from event_log where event_time>?"
	if qevent_time_end > 0 {
		sql = sql + " AND event_time<?"
		params[idx] = qevent_time_end
		idx++
	}
	if qtype_ != "" {
		sql = sql + " AND type_=?"
		params[idx] = qtype_
		idx++
	}
	if qname != "" {
		sql = sql + " AND name=?"
		params[idx] = qname
		idx++
	}

	sql = sql + " LIMIT 20"
	stmt, err := db.Prepare(sql)
	if err != nil {
		log.Errorf("------db Prepare err: %v", err)
		return nil, err
	}
	defer stmt.Close()
	rows, err := stmt.Query(params[:idx]...)
	if err != nil {
		log.Errorf("------db Query err: %v", err)
		return nil, err
	}

	list := make([]EventLog, 20)
	var nRows int
	for rows.Next() {
		var uid int64
		var event_time int64
		var type_ string
		var name string
		var desc string
		var log_name string

		err := rows.Scan(&uid, &event_time, &type_, &name, &desc, &log_name)
		if err != nil {
			log.Errorf("------db rows.Scan err: %v", err)
			return nil, err
		}
		el := &EventLog{
			Uid:       uid,
			EventTime: event_time,
			Type:      type_,
			Name:      name,
			Desc:      desc,
			LogName:   log_name,
		}
		list[nRows] = *el
		nRows++
	}

	if err := rows.Err(); err != nil {
		log.Errorf("db err: %v", err)
	}
	log.Printf("Total %d rows for %s, param: %v", nRows, sql, params[:idx])
	list = list[:nRows]

	jsonByte, _ := json.Marshal(list)
	jsonStr := string(jsonByte)
	log.Printf("%v", jsonStr)

	return list, nil
}

func insertDB(event_time int64, type_, name, desc, log_name string, alarm bool) {
	if alarm {
		AddAlarm(EventLog{
			EventTime: event_time,
			Type:      type_,
			Name:      name,
			Desc:      desc,
			LogName:   log_name,
		})
	}
	filePath := dbFilePath
	db, _ := sql.Open("sqlite3", filePath)
	stmt, err := db.Prepare("insert into event_log(event_time, type_, name, desc, log_name) values(?, ?, ?, ?, ?)")
	if err != nil {
		log.Errorf(name+" can not Prepare db, err: %v", err)
		tryTimes := 0
		for {
			time.Sleep(100)
			stmt, err = db.Prepare("insert into event_log(event_time, type_, name, desc, log_name) values(?, ?, ?, ?, ?)")
			if err == nil || tryTimes > 100 {
				break
			}
			tryTimes++
		}
	}
	defer stmt.Close()
	_, err = stmt.Exec(event_time, type_, name, desc, log_name)
	if err != nil {
		log.Errorf(name+" can not Exec db, err: %v", err)
		tryTimes := 0
		for {
			time.Sleep(100)
			_, err = stmt.Exec(event_time, type_, name, desc, log_name)
			if err == nil || tryTimes > 100 {
				break
			}
			tryTimes++
		}
	}
	//id, err := res.LastInsertId()
}

func getEventConfig(e_type string, c *config.Entry) *lib.EventConfig {
	if c == nil {
		return nil
	}
	for _, ev := range c.Program.Event {
		if ev.Type == e_type {
			return &ev
		}
	}
	// 使用默认值，如果配置了default
	for _, ev := range c.Program.Event {
		if ev.Type == "default" {
			return &ev
		}
	}
	return nil
}

func getStdoutLogfile2(c *config.Config) string {
	fileName := c.ParseConfigStringExpression(c.Config.Buddhad.Logfile)
	expandFile, err := tool.PathExpand(fileName)
	if err != nil {
		return fileName
	}
	return expandFile
}
